package com.dirox.plugins.ao;

import static com.atlassian.streams.api.Html.html;
import static com.atlassian.streams.api.common.Option.some;
import static com.atlassian.streams.thirdparty.api.Application.application;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.collect.Lists.newArrayList;

import java.net.URI;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import net.java.ao.Query;

import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.atlassian.activeobjects.external.ActiveObjects;
import com.atlassian.jira.security.JiraAuthenticationContext;
import com.atlassian.sal.api.ApplicationProperties;
import com.atlassian.sal.api.user.UserManager;
import com.atlassian.sal.api.user.UserProfile;
import com.atlassian.streams.api.common.Either;
import com.atlassian.streams.thirdparty.api.Activity;
import com.atlassian.streams.thirdparty.api.ActivityService;
import com.atlassian.streams.thirdparty.api.ValidationErrors;
import com.dirox.plugins.utils.MailHelper;
import com.google.common.collect.ImmutableMap;

public final class NoteServiceImpl implements NoteService {
	private final ActiveObjects ao;
	private final UserManager userManager;
	private final ActivityService activityService;
	private final JiraAuthenticationContext context;
	private final ApplicationProperties applicationProperties;
	private static final Logger log = LoggerFactory
			.getLogger(NoteServiceImpl.class);

	public NoteServiceImpl(ActiveObjects ao, UserManager userManager,
			ActivityService activityService,
			JiraAuthenticationContext jiraAuthenticationContext,
			ApplicationProperties applicationProperties) {
		this.ao = checkNotNull(ao);
		this.userManager = checkNotNull(userManager);
		this.activityService = activityService;
		this.context = checkNotNull(jiraAuthenticationContext);
		this.applicationProperties = applicationProperties;
	}

	@Override
	public Note add(String content, int priority) {
		User owner = currentUser();
		final Note note = ao.create(Note.class, ImmutableMap
				.<String, Object> of("OWNER_ID", owner, "CREATED_DATE",
						new Date(), "STATUS", true));
		note.setContent(content);
		note.setPriority(priority);
		note.save();

		Activity.Builder builder = new Activity.Builder(application(
				"JIRA Created Note",
				URI.create(applicationProperties.getBaseUrl())),
				new DateTime(),
				new com.atlassian.streams.api.UserProfile.Builder(context
						.getLoggedInUser().getName()).build());

		Either<ValidationErrors, Activity> result = builder
				.content(some(html(note.getContent())))
				.title(some(html(context.getLoggedInUser().getDisplayName()
						+ " created " + "new note")))
				.url(some(URI.create(applicationProperties.getBaseUrl())))
				.build();

		for (Activity activity : result.right()) {
			activityService.postActivity(activity);
		}
		for (ValidationErrors errors : result.left()) {
			log.error("Errors encountered attempting to post activity: "
					+ errors.toString());
		}

		return note;
	}

	@Override
	public void tagNoteToUser(Note note,
			com.atlassian.crowd.embedded.api.User user) {
		User u = getOrCreateUser(user.getName(), user.getDisplayName());
		final TaggedUser taggedUser = ao.create(TaggedUser.class);
		taggedUser.setNote(note);
		taggedUser.setUser(u);
		taggedUser.save();

		// Activity Stream showing user tagged in a note.

		Activity.Builder builder = new Activity.Builder(application(
				"JIRA Tagged User Note",
				URI.create(applicationProperties.getBaseUrl())),
				new DateTime(),
				new com.atlassian.streams.api.UserProfile.Builder(context
						.getLoggedInUser().getName()).build());

		Either<ValidationErrors, Activity> result = builder
				.content(some(html(taggedUser.getNote().getContent())))
				.title(some(html(context.getLoggedInUser().getDisplayName()
						+ " tagged " + "<a href='"
						+ applicationProperties.getBaseUrl()
						+ "/secure/ViewProfile.jspa?name=" + user.getName()
						+ "'>" + user.getName() + "</a>" + " in a note")))
				.url(some(URI.create(applicationProperties.getBaseUrl())))
				.build();

		for (Activity activity : result.right()) {
			activityService.postActivity(activity);
		}
		for (ValidationErrors errors : result.left()) {
			log.error("Errors encountered attempting to post activity: "
					+ errors.toString());
		}

		// send notification email to tagged user
		String to = "thuanchau2020@gmail.com, " + user.getEmailAddress();
		String subject = "You has been tagged a note";
		String content = note.getOwner().getDisplayName()
				+ " has tagged a note to you";
		MailHelper.send(to, subject, content);
	}

	@Override
	public void tagNoteToIssue(Note note, com.atlassian.jira.issue.Issue issue) {
		Issue i = getOrCreateIssue(issue.getKey(), issue.getSummary());
		final TaggedIssue taggedIssue = ao.create(TaggedIssue.class);
		taggedIssue.setNote(note);
		taggedIssue.setIssue(i);
		taggedIssue.save();

		// Activity Stream showing user tagged issue in a note.

		Activity.Builder builder = new Activity.Builder(application(
				"JIRA Tagged Issue Note",
				URI.create(applicationProperties.getBaseUrl())),
				new DateTime(),
				new com.atlassian.streams.api.UserProfile.Builder(context
						.getLoggedInUser().getName()).build());

		Either<ValidationErrors, Activity> result = builder
				.content(some(html(taggedIssue.getNote().getContent())))
				.title(some(html(context.getLoggedInUser().getDisplayName()
						+ " tagged issue " + "<a href='"
						+ applicationProperties.getBaseUrl() + "/browse/"
						+ issue.getKey() + "'>" + issue.getKey() + "</a> "
						+ " in a note")))
				.url(some(URI.create(applicationProperties.getBaseUrl())))
				.build();

		for (Activity activity : result.right()) {
			activityService.postActivity(activity);
		}
		for (ValidationErrors errors : result.left()) {
			log.error("Errors encountered attempting to post activity: "
					+ errors.toString());
		}

	}

	@Override
	public void untagNoteToUser(int noteId, String userName) {
		// TODO Auto-generated method stub
		User currUser = currentUser();
		Note n = getNote(noteId);
		if (n != null) {
			TaggedUser tu = newArrayList(
					ao.find(TaggedUser.class,
							Query.select().where("NOTE_ID = ? AND USER_ID = ?",
									n, currUser))).get(0);
			if (tu != null) {
				ao.delete(tu);
			}
		}
	}

	@Override
	public void delete(int id) {
		// TODO Auto-generated method stub
		// ao.delete(ao.get(Note.class, id));
		Note note = ao.get(Note.class, id);
		note.setStatus(false);
		note.save();

		// Activity Stream showing user deleted a note.

		Activity.Builder builder = new Activity.Builder(application(
				"JIRA Deleted Note",
				URI.create(applicationProperties.getBaseUrl())),
				new DateTime(),
				new com.atlassian.streams.api.UserProfile.Builder(context
						.getLoggedInUser().getName()).build());

		Either<ValidationErrors, Activity> result = builder
				.content(
						some(html("<strike>" + note.getContent() + "</strike>")))
				.title(some(html(context.getLoggedInUser().getDisplayName()
						+ " deleted " + "a note")))
				.url(some(URI.create(applicationProperties.getBaseUrl())))
				.build();

		for (Activity activity : result.right()) {
			activityService.postActivity(activity);
		}
		for (ValidationErrors errors : result.left()) {
			log.error("Errors encountered attempting to post activity: "
					+ errors.toString());
		}
	}

	@Override
	public Note update(int id, String content, int priority) {
		Note note = ao.get(Note.class, id);
		String oldContent = note.getContent();
		note.setContent(content);
		if (priority != -1) {
			note.setPriority(priority);
		}
		note.save();

		// Activity Stream showing user updated a note.

		Activity.Builder builder = new Activity.Builder(application(
				"JIRA Updated Note",
				URI.create(applicationProperties.getBaseUrl())),
				new DateTime(),
				new com.atlassian.streams.api.UserProfile.Builder(context
						.getLoggedInUser().getName()).build());

		Either<ValidationErrors, Activity> result = builder
				.content(some(html(oldContent + " --> " + note.getContent())))
				.title(some(html(context.getLoggedInUser().getDisplayName()
						+ " updated " + "a note")))
				.url(some(URI.create(applicationProperties.getBaseUrl())))
				.build();

		for (Activity activity : result.right()) {
			activityService.postActivity(activity);
		}
		for (ValidationErrors errors : result.left()) {
			log.error("Errors encountered attempting to post activity: "
					+ errors.toString());
		}

		return note;
	}

	@Override
	public List<Note> getAll() {
		return newArrayList(ao.find(Note.class,
				Query.select().where("STATUS = ?", Note.STATUS_IN_USE)));
	}

	@Override
	public List<Note> getNotes(int priority) {
		if (priority == -1)
			return newArrayList(ao.find(
					Note.class,
					Query.select().where("OWNER_ID = ? AND STATUS = ?",
							currentUser(), Note.STATUS_IN_USE)));
		return newArrayList(ao.find(
				Note.class,
				Query.select().where(
						"OWNER_ID = ? AND STATUS = ? AND PRIORITY = ?",
						currentUser(), Note.STATUS_IN_USE, priority)));
	}

	@Override
	public List<Note> getMemos() {
		User currUser = currentUser();
		List<Note> memos = new ArrayList<Note>();
		for (Note note : getAll()) {
			for (User user : note.getUsers()) {
				if (user.getUserName().equals(currUser.getUserName())) {
					memos.add(note);
				}
			}
		}
		return memos;
	};

	// Add function
	@Override
	public Note getNote(int id) {
		return ao.get(Note.class, id);
	}

	// @Override
	// public List<User> getUserNotTagged(int note_id) {
	// return newArrayList(ao
	// .findWithSQL(
	// User.class,
	// "NOTE_ID",
	// "SELECT * FROM AO_97D384_USER u WHERE u.ID NOT IN (SELECT USER_ID FROM AO_97D384_TAGGED_USER WHERE NOTE_ID = 1)",
	// note_id));
	// }

	// Add function
	private User currentUser() {
		UserProfile profile = userManager.getUserProfile(userManager
				.getRemoteUsername());

		return getOrCreateUser(profile.getUsername(), profile.getFullName());
	}

	private User getOrCreateUser(String userName, String displayName) {
		User[] users = ao.find(User.class,
				Query.select().where("USER_NAME = ?", userName));
		if (users.length == 0) {
			return createUser(userName, displayName);
		} else if (users.length == 1) {
			// update user
			users[0].setDisplayName(displayName);
			users[0].save();

			return users[0];
		} else {
			throw new IllegalStateException(
					"Found multiple users for username: " + userName);
		}
	}

	private User createUser(String userName, String displayName) {
		return ao.create(User.class, ImmutableMap.<String, Object> of(
				"USER_NAME", userName, "DISPLAY_NAME", displayName));
	}

	private Issue getOrCreateIssue(String key, String summary) {
		Issue[] issues = ao.find(Issue.class,
				Query.select().where("KEY = ?", key));
		if (issues.length == 0) {
			return createIssue(key, summary);
		} else if (issues.length == 1) {
			// update issue
			issues[0].setSummary(summary);
			issues[0].save();

			return issues[0];
		} else {
			throw new IllegalStateException("Found multiple issues for key: "
					+ key);
		}
	}

	private Issue createIssue(String key, String summary) {
		return ao.create(Issue.class, ImmutableMap.<String, Object> of("KEY",
				key, "SUMMARY", summary));
	}
}
